Gabriel Chaix 50% | Elsa Chevrot 50%

TD 8/9 : Analyse des données

Analyse des valeurs foncières

Dataset OpenData :

data.gouv.fr/demandes-de-valeurs-foncieres


Il s'agit des transactions immobilières intervenues au cours des cinq dernières années sur le territoire métropolitain et les DOM-TOM, à l’exception de l’Alsace, de la Moselle et de Mayotte. Les données contenues sont issues des actes notariés et des informations cadastrales.


Rapport

Avancement

Pour commencer cette analyse de données, nous avons tout d’abord importé les librairies nécessaires pour notre projet comme, par exemple, numpy, pandas, plotly et d’autres.
Par la suite, nous avons chargé les données de valeurs foncières de 2022.
Ensuite, nous avons procédé au nettoyage des données du ficher afin d’éliminer celles qui n’étaient pas pertinentes ou gênantes pour notre étude. Ainsi, nous avons vérifié les parts des données manquantes dans la base brutes afin de supprimer celles vides, soit celles à 100% après calcul (suppression de 14 colonnes). Nous avons également supprimé les doubles s’il y en avait et les lignes null. Après cela, nous avons corrigé les types de nos données afin de pouvoir les utiliser plus facilement par la suite et nous avons supprimé les valeurs foncières null. Nous sommes parvenus à un dataset de 29 colonnes. Ce nettoyage nous a donc permis de réduire le nombre de données à traiter.
Après tout ce travail de nettoyage, nous avons pu réaliser les graphes suivants sur les données de 2022 :
- Part de maison, d'appartement, ect.. dans le dataset
- Prix au mètre carré en fonction du type de local
- Comparaison de plusieurs types de local
- Surface du terrain par département
- Valeur foncière moyenne par département en 2022 (bar plot)
- Valeur foncière moyenne par département en 2022 (geojson et choropleth)
- Valeur foncière moyenne par pays, par département et par commune (treemap) NE FONCTIONNE PAS
- Surface réelle bâtie moyenne par département
- Départements les plus chers pour l'immobilier en 2022
- Valeur foncière moyenne par jour en 2022
- Valeur foncière moyenne à Paris en 2022
- Prix moyen du mètre carré pour les appartements à Paris en 2022
- Nombre de bien vendu dans les 20 villes
- Valeur foncière moyenne par arrondissement de Lyon en 2022
- Nombre de biens vendus par arrondissement de Lyon en 2022
- Nature des cultures en France en 2022
- Relation entre valeur foncière et nature de mutation
- Relation entre nombre de pièce principale et nature de mutation
- Prix au m² selon le type de local
- Prix au m² selon le nombre de pièce principale
Puis nous avons réitéré l’opération sur les valeurs foncières de 2021 pour les comparer avec celles de 2022 afin de réaliser les graphes suivants :
- Comparaison du prix moyen du mètre carré entre 2021 et 2022
- Comparaison de la nature des mutations entre 2021 et 2022
- Surface du terrain en fonction du département entre 2021 et 2022
- Relation entre la commune et la valeur foncière en fonction de la commune entre 2021 et 2022
- Nombre de biens vendus par commune en 2021 et 2022

Django

Difficultés

Nous avons rencentré quelques difficultés au cours de ce projet. Premièrement, dans la base de données, il n’y a pas d’explication précise sur quelle colonne correspond à quoi et pas de pré indication sur certaines données (exemple : certains prix astronomiques au m² à Paris, alors que cela dépend simplement de l’arrondissement mais n’est pas précisé).
Sur l’utilisation du framework django, nous avons A DETAILLER QUAND ON L’AURA FAIT + Importation du dataset de 2021

Contribution des membres

La répartition des tâches fut assez équilibrée. Nous avons commencé par importer et nettoyer le fichier ensemble en TD, puis nous nous sommes réparti les différents graphes à faire en 2 parties, une partie chacun. Nous avons travaillé en ligne sur Google Colab afin de pouvoir suivre les avancées de l’autre sur le notebook.
Pour l’export html et la partie django, nous nous sommes retrouvés pour comprendre son fonctionnement et le réaliser ensemble.
Gabriel a réalisé la partie comparaison des données avec 2021 et Elsa a réalisé le rapport. Nous sommes donc à 50% de participation chacun !

Conclusion

Nous avons trouvé ce projet intéressant, du fait que l’on traite de donnés réelles, et qui nous parlent. Il nous a permis d’exploiter nos connaissances en Python avec le notebook, mais également à traiter un jeu de données en choisissant les plus pertinente.

Librairies

Install

%load_ext pretty_jupyter
%load_ext jinja2
The jinja2 module is not an IPython extension.
# install calmap pour colab
#! pip install calmap
#mettre à jour plotly pour colab
#!pip install plotly==4.5.2
#!pip install plotly
#!pip install geopandas
#!pip install jinja2 

Imports

# librairies essentielles
import json
import requests
import random
from urllib.request import urlopen
from  collections import Counter

# analyse et stockage
import numpy as np
import pandas as pd

# visualisation
import warnings
warnings.filterwarnings('ignore')
import matplotlib.pyplot as plt
import seaborn as sns
import plotly.express as px
import geopandas as gpd
colors1 = ['#DAF7A6', '#FFC300', '#FF5733', '#C70039', '#900C3F', '#581845']
colors2 = ["#e60049", "#0bb4ff", "#50e991", "#e6d800", "#9b19f5", "#ffa300", "#dc0ab4", "#b3d4ff", "#00bfa0"]
colors3 = ["#1984c5", "#22a7f0", "#63bff0", "#a7d5ed", "#e2e2e2", "#e1a692", "#de6e56", "#e14b31", "#c23728"]

I ] Chargement

df = pd.read_csv('https://static.data.gouv.fr/resources/demandes-de-valeurs-foncieres/20230405-160733/valeursfoncieres-2022.txt', sep = '|')
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 3803885 entries, 0 to 3803884
Data columns (total 43 columns):
 #   Column                      Dtype  
---  ------                      -----  
 0   Identifiant de document     float64
 1   Reference document          float64
 2   1 Articles CGI              float64
 3   2 Articles CGI              float64
 4   3 Articles CGI              float64
 5   4 Articles CGI              float64
 6   5 Articles CGI              float64
 7   No disposition              int64  
 8   Date mutation               object 
 9   Nature mutation             object 
 10  Valeur fonciere             object 
 11  No voie                     float64
 12  B/T/Q                       object 
 13  Type de voie                object 
 14  Code voie                   object 
 15  Voie                        object 
 16  Code postal                 float64
 17  Commune                     object 
 18  Code departement            object 
 19  Code commune                int64  
 20  Prefixe de section          float64
 21  Section                     object 
 22  No plan                     int64  
 23  No Volume                   object 
 24  1er lot                     object 
 25  Surface Carrez du 1er lot   object 
 26  2eme lot                    object 
 27  Surface Carrez du 2eme lot  object 
 28  3eme lot                    object 
 29  Surface Carrez du 3eme lot  object 
 30  4eme lot                    float64
 31  Surface Carrez du 4eme lot  object 
 32  5eme lot                    float64
 33  Surface Carrez du 5eme lot  object 
 34  Nombre de lots              int64  
 35  Code type local             float64
 36  Type local                  object 
 37  Identifiant local           float64
 38  Surface reelle bati         float64
 39  Nombre pieces principales   float64
 40  Nature culture              object 
 41  Nature culture speciale     object 
 42  Surface terrain             float64
dtypes: float64(17), int64(4), object(22)
memory usage: 1.2+ GB
# formatage des noms des colonnes
df = df.rename(columns=lambda x: x.replace(' ', '_').lower())

II ] Nettoyage

#Vérifier les parts des données manquantes dans la base brutes
df.isnull().sum()/len(df)*100
identifiant_de_document       100.000000
reference_document            100.000000
1_articles_cgi                100.000000
2_articles_cgi                100.000000
3_articles_cgi                100.000000
4_articles_cgi                100.000000
5_articles_cgi                100.000000
no_disposition                  0.000000
date_mutation                   0.000000
nature_mutation                 0.000000
valeur_fonciere                 0.649888
no_voie                        34.733621
b/t/q                          95.374019
type_de_voie                   36.984294
code_voie                       1.026398
voie                            1.029290
code_postal                     1.029816
commune                         0.000000
code_departement                0.000000
code_commune                    0.000000
prefixe_de_section             95.824295
section                         0.004075
no_plan                         0.000000
no_volume                      99.765792
1er_lot                        66.631142
surface_carrez_du_1er_lot      90.332568
2eme_lot                       89.535278
surface_carrez_du_2eme_lot     96.741752
3eme_lot                       98.134670
surface_carrez_du_3eme_lot     99.635662
4eme_lot                       99.402611
surface_carrez_du_4eme_lot     99.911880
5eme_lot                       99.744130
surface_carrez_du_5eme_lot     99.967481
nombre_de_lots                  0.000000
code_type_local                39.918478
type_local                     39.918478
identifiant_local             100.000000
surface_reelle_bati            39.973396
nombre_pieces_principales      39.973396
nature_culture                 33.830912
nature_culture_speciale        95.934262
surface_terrain                33.830912
dtype: float64
# suppression des attributs non nécessaire
df = df.drop(['identifiant_de_document',
              'reference_document',
              '1_articles_cgi',
              '2_articles_cgi',
              '3_articles_cgi',
              '4_articles_cgi',
              '5_articles_cgi',
              'prefixe_de_section',
              'no_volume',
              '3eme_lot',
              '4eme_lot',
              '5eme_lot',
              'identifiant_local',
              'nature_culture_speciale',
              ],axis= 1)
# suppression des doubles
df = df.drop_duplicates()
# suppression des ligne null
df = df.dropna(how="all")
# correction des types de données
df['date_mutation'] = pd.to_datetime(df['date_mutation'])
df['nature_mutation'] = df['nature_mutation'].astype('category')
df['valeur_fonciere'] = df['valeur_fonciere'].str.replace(',', '.').astype('float')
df['no_voie'] = df['no_voie'].astype('Int32')
df['b/t/q'] = df['b/t/q'].astype('category')
df['code_postal'] = df['code_postal'].astype('float')
df['code_commune'] = df['code_commune'].astype('int32')
df['no_plan'] = df['no_plan'].astype('int32')
df['1er_lot'] = df['1er_lot'].astype('category')
df['2eme_lot'] = df['2eme_lot'].astype('category')
df['surface_carrez_du_1er_lot'] = df['surface_carrez_du_1er_lot'].str.replace(',', '.').astype('float')
df['surface_carrez_du_2eme_lot'] = df['surface_carrez_du_2eme_lot'].str.replace(',', '.').astype('float')
df['surface_carrez_du_3eme_lot'] = df['surface_carrez_du_3eme_lot'].str.replace(',', '.').astype('float')
df['surface_carrez_du_4eme_lot'] = df['surface_carrez_du_4eme_lot'].str.replace(',', '.').astype('float')
df['surface_carrez_du_5eme_lot'] = df['surface_carrez_du_5eme_lot'].str.replace(',', '.').astype('float')
df['nombre_de_lots'] = df['nombre_de_lots'].astype('int32')
df['code_type_local'] = df['code_type_local'].astype('Int32')
df['type_local'] = df['type_local'].astype('category')
df['surface_reelle_bati'] = df['surface_reelle_bati'].astype('float')
df['nombre_pieces_principales'] = df['nombre_pieces_principales'].astype('Int32')
df['nature_culture'] = df['nature_culture'].astype('category')
df['surface_terrain'] = df['surface_terrain'].astype('float')
#remplissage des valeur null

df['surface_carrez_du_1er_lot'].fillna(0, inplace=True)
df['surface_carrez_du_2eme_lot'].fillna(0, inplace=True)
df['surface_carrez_du_3eme_lot'].fillna(0, inplace=True)
df['surface_carrez_du_4eme_lot'].fillna(0, inplace=True)
df['surface_carrez_du_5eme_lot'].fillna(0, inplace=True)
df['surface_reelle_bati'].fillna(0, inplace=True)
df['surface_terrain'].fillna(0, inplace=True)
df['valeur_fonciere'].fillna(0, inplace=True)
df['nombre_pieces_principales'].fillna(0, inplace=True)
df = df[df['valeur_fonciere'] > 0]
df = df[df['surface_reelle_bati'] > 0]
df = df.loc[(df['code_departement'] != 22) | (~df.duplicated(subset=['valeur_fonciere'], keep=False))]
df.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 1215482 entries, 0 to 3803882
Data columns (total 29 columns):
 #   Column                      Non-Null Count    Dtype         
---  ------                      --------------    -----         
 0   no_disposition              1215482 non-null  int64         
 1   date_mutation               1215482 non-null  datetime64[ns]
 2   nature_mutation             1215482 non-null  category      
 3   valeur_fonciere             1215482 non-null  float64       
 4   no_voie                     1208562 non-null  Int32         
 5   b/t/q                       83276 non-null    category      
 6   type_de_voie                1091558 non-null  object        
 7   code_voie                   1215482 non-null  object        
 8   voie                        1215453 non-null  object        
 9   code_postal                 1215452 non-null  float64       
 10  commune                     1215482 non-null  object        
 11  code_departement            1215482 non-null  object        
 12  code_commune                1215482 non-null  int32         
 13  section                     1215424 non-null  object        
 14  no_plan                     1215482 non-null  int32         
 15  1er_lot                     482819 non-null   category      
 16  surface_carrez_du_1er_lot   1215482 non-null  float64       
 17  2eme_lot                    196721 non-null   category      
 18  surface_carrez_du_2eme_lot  1215482 non-null  float64       
 19  surface_carrez_du_3eme_lot  1215482 non-null  float64       
 20  surface_carrez_du_4eme_lot  1215482 non-null  float64       
 21  surface_carrez_du_5eme_lot  1215482 non-null  float64       
 22  nombre_de_lots              1215482 non-null  int32         
 23  code_type_local             1215482 non-null  Int32         
 24  type_local                  1215482 non-null  category      
 25  surface_reelle_bati         1215482 non-null  float64       
 26  nombre_pieces_principales   1215482 non-null  Int32         
 27  nature_culture              727284 non-null   category      
 28  surface_terrain             1215482 non-null  float64       
dtypes: Int32(3), category(6), datetime64[ns](1), float64(9), int32(3), int64(1), object(6)
memory usage: 208.0+ MB
df.head()
no_disposition date_mutation nature_mutation valeur_fonciere no_voie b/t/q type_de_voie code_voie voie code_postal ... surface_carrez_du_3eme_lot surface_carrez_du_4eme_lot surface_carrez_du_5eme_lot nombre_de_lots code_type_local type_local surface_reelle_bati nombre_pieces_principales nature_culture surface_terrain
0 1 2022-03-01 Vente 55000.0 13 NaN RUE 2280 DE LA LIBERTE 1000.0 ... 0.0 0.0 0.0 1 2 Appartement 24.0 1 NaN 0.0
3 1 2022-03-01 Vente 143000.0 98 NaN RTE 0055 DE LA DOMBES 1480.0 ... 0.0 0.0 0.0 1 2 Appartement 140.0 3 NaN 0.0
5 1 2022-06-01 Vente 255000.0 282 NaN RTE 0130 DE POISATON 1560.0 ... 0.0 0.0 0.0 0 1 Maison 108.0 5 S 649.0
8 1 2022-03-01 Vente 525000.0 217 NaN PL 0300 DE LA CROIX BLANCHE 1390.0 ... 0.0 0.0 0.0 0 4 Local industriel. commercial ou assimilé 424.0 0 S 628.0
9 1 2022-03-01 Vente 525000.0 217 NaN PL 0300 DE LA CROIX BLANCHE 1390.0 ... 0.0 0.0 0.0 0 2 Appartement 126.0 4 S 628.0

5 rows × 29 columns

III ] Interprétations

Générales

  1. Quelle est la part de maison, d'appartement, ect.. dans le dataset?
# Quelle est la part de maison, d'appartement, ect.. dans le dataset?
types = df['type_local'].value_counts(dropna = False, normalize = True).head()
plt.figure(figsize=(8,8))
plt.pie(types.values, labels=types.index, colors = colors2, startangle=65)
plt.title(label= 'Parts des types de biens')
plt.legend(loc = 4)
<matplotlib.legend.Legend at 0x7f955e772940>
  1. Prix au mètre carré en fonction du type de local
# Prix au mètre carré en fonction du type de local
pm2 = df[(df['nombre_de_lots']<6) & ((df['surface_carrez_du_1er_lot'] + df['surface_carrez_du_2eme_lot'] + df['surface_carrez_du_3eme_lot'] + df['surface_carrez_du_4eme_lot'] + df['surface_carrez_du_5eme_lot']) > 0)]
pm2['surface_totale'] = pm2['surface_carrez_du_1er_lot'] + pm2['surface_carrez_du_2eme_lot'] + pm2['surface_carrez_du_3eme_lot'] + pm2['surface_carrez_du_4eme_lot'] + pm2['surface_carrez_du_5eme_lot']
pm2['prix_m2'] = pm2['valeur_fonciere'] / pm2['surface_totale']
sns.set(rc={'figure.figsize':(40,30)})
sns.color_palette()
sns.catplot(x="type_local", y="prix_m2", jitter=True, data=pm2, kind="strip", height=8, aspect=1.5, palette="Set2")
#sns.stripplot(pm2['type_local'],pm2['prix_m2'],jitter=True)
#pm2.loc[:,['type_local','prix_m2']].plot(kind='scatter',x='type_local',y='prix_m2')
<seaborn.axisgrid.FacetGrid at 0x7f9548dcd9a0>
  1. Comparaison de plusieurs types de local
prixlocal = df.loc[df['type_local'].notna() & df['surface_terrain'].notna() & df['surface_terrain'] >= 1 & df['valeur_fonciere'].notna(),("surface_terrain","valeur_fonciere","type_local")]
prixlocal['prix_m2_sur_le_terrain'] = prixlocal['valeur_fonciere']/prixlocal['surface_terrain']
prixlocal['valeur_fonciere'] = prixlocal['valeur_fonciere']/1000
prixlocal.groupby(['type_local']).mean().plot.bar(title = 'Comparaison de différents types de local',figsize=(20,5))
<AxesSubplot:title={'center':'Comparaison de différents types de local'}, xlabel='type_local'>
  1. Surface du terrain par département
fig = plt.figure(4,figsize = (30,5))
axis = fig.add_subplot(1, 1, 1)
axis.set_title('Surface du terrain en fonction du département')
axis.set_xlabel("Département")
axis.set_ylabel("Surface du terrain")
chmp = df.loc[df["code_departement"].notna(),("surface_terrain","code_departement")]
gb = chmp.groupby("code_departement",dropna= False).mean()['surface_terrain'].reset_index()

axis.bar(gb["code_departement"].astype(str), gb["surface_terrain"])
plt.xticks(rotation=90)
fig.show()
  1. Quelle est la valeur foncière moyenne par département en 2022 (bar plot) ?
#Quelle est la valeur foncière moyenne par departement en utilisant un bar plot ?
grouped_vf_dept = df.groupby('code_departement')['valeur_fonciere'].mean().reset_index()
grouped_vf_dept['code_departement'] = grouped_vf_dept['code_departement'].astype('string')
fig = px.bar(grouped_vf_dept, y='valeur_fonciere',
             x='code_departement',
             text_auto='.2s')
fig.update_layout(title="Valeur foncière moyenne par département", 
                  yaxis_title='Valeur foncière moyenne (€)', 
                  xaxis_title='Département');
%%jmd
[//]: # (-.-|m { input: true, output_error: false, input_fold: hide })
{{fig.to_html(include_plotlyjs=False, full_html=False)}}
  1. Quelle est la valeur foncière moyenne par département en 2022 (geojson et choropleth) ?
#Quelle est la valeur foncière moyenne par département en 2022 (geojson et choropleth) ?
geojson_url = "https://france-geojson.gregoiredavid.fr/repo/departements.geojson"
geojson_data = requests.get(geojson_url).json()

fig = px.choropleth(grouped_vf_dept, 
                    geojson=geojson_data, 
                    locations='code_departement', 
                    color='valeur_fonciere',
                    color_continuous_scale='pinkyl',
                    featureidkey='properties.code',
                    projection="mercator",
                    title='Valeur foncière moyenne par départements')
fig.update_geos(fitbounds="locations", visible=False)
fig.update_layout(height=600, width=800);
%%jmd
[//]: # (-.-|m { input: true, output_error: false, input_fold: hide })
{{ fig.to_html(include_plotlyjs=False, full_html=False) }}
  1. Quelle est la valeur foncière moyenne par pays, par département et par commune (treemap) ?
# Quelle est la valeur foncière moyenne par pays, par département (treemap) ?
df_avg = df.groupby(['code_departement'])['valeur_fonciere'].mean().reset_index()
fig = px.treemap(df_avg,
                 path=[px.Constant("France"),'code_departement'],
                 values='valeur_fonciere',
                 color='valeur_fonciere')
fig.update_layout(title='Carte proportionelle de la valeur foncière moyenne');
#fig.show()
%%jmd
[//]: # (-.-|m { input: true, output_error: false, input_fold: hide })
{{ fig.to_html(include_plotlyjs=False, full_html=False) }}

On s'aperçoit que la valeur foncière est fortement correlée avec la position géographique.

  1. Quelle est la surface réelle bâtie moyenne par département?
# Quelle est la surface réelle bâtie moyenne par département?
grouped_srb_dept = df[df['surface_reelle_bati'] > 0]
grouped_srb_dept = grouped_srb_dept.groupby('code_departement')['surface_reelle_bati'].mean().reset_index()
grouped_srb_dept['code_departement'] = grouped_srb_dept['code_departement'].astype('string')
fig = px.bar(grouped_srb_dept, y='surface_reelle_bati',
             x='code_departement',
             text_auto='.2s')
fig.update_layout(title="Surface réelle bâtie moyenne par departement", 
                  yaxis_title='Valeur foncière moyenne (€)', 
                  xaxis_title='Département');
#fig.show()
%%jmd
[//]: # (-.-|m { input: true, output_error: false, input_fold: hide })
{{ fig.to_html(include_plotlyjs=False, full_html=False) }}
  1. Quels sont les départements les plus chers pour l'immobilier en 2022 ?
# Quels sont les départements les plus chers pour l'immobilier en 2022 ?
grouped_vf_dept_top10 = grouped_vf_dept.sort_values('valeur_fonciere', ascending=False).head(10)

fig = px.bar(grouped_vf_dept_top10, x='valeur_fonciere', y='code_departement', orientation='h')

fig.update_layout(title='Les départements les plus chers pour l\'immobilier en 2022', 
                  xaxis_title='Valeur foncière moyenne (€)', 
                  yaxis_title='Département');
#fig.show()
%%jmd
[//]: # (-.-|m { input: true, output_error: false, input_fold: hide })
{{ fig.to_html(include_plotlyjs=False, full_html=False) }}
  1. Valeur foncière moyenne par jour en 2022
# Valeur foncière moyenne par jour en 2022
filtered_df = df[df['valeur_fonciere'] <= 4000000]
daily_means = filtered_df.groupby(filtered_df['date_mutation'].dt.date)['valeur_fonciere'].mean()

plt.scatter(daily_means.index, daily_means.values, c='red', label='Prix moyen')

avg_value = df['valeur_fonciere'].mean()
plt.axhline(y=avg_value, color='green', linestyle='solid', label='Valeur foncière moyenne sur 2022')

tick_dates = pd.date_range(start='2022-01-01', end='2022-12-31', freq='2M')
tick_labels = [date.strftime('%Y-%m') for date in tick_dates]
plt.xticks(ticks=tick_dates, labels = tick_labels, rotation=80)

plt.title('Valeur foncière moyenne par jour en 2022')
plt.xlabel("Date")
plt.ylabel("Valeur foncière moyenne")

plt.legend()
plt.show()
  1. Quelle est la valeur foncière moyenne à Paris en 2022 ?
#Quelle est la valeur foncière moyenne par commune dans le 75 en utilisant geojson et choropleth ?

geojson_url = "https://france-geojson.gregoiredavid.fr/repo/departements/75-paris/communes-75-paris.geojson"
geojson_data = requests.get(geojson_url).json()

vf_com_paris = df[(df['code_departement']==75) & df['valeur_fonciere'] > 0]
vf_com_paris = vf_com_paris.groupby('code_commune')['valeur_fonciere'].mean().reset_index()
vf_com_paris['code_commune'] = '75'+vf_com_paris['code_commune'].astype(str)

fig = px.choropleth(vf_com_paris, 
                    geojson=geojson_data, 
                    locations='code_commune', 
                    color='valeur_fonciere',
                    color_continuous_scale='Reds',
                    featureidkey='properties.code',
                    projection="mercator",
                    title="Valeur fonciére moyenne en €")
fig.update_geos(fitbounds="locations", visible=False)
fig.update_layout(height=600,
                  width=800);
#fig.show()
%%jmd
[//]: # (-.-|m { input: true, output_error: false, input_fold: hide })
{{ fig.to_html(include_plotlyjs=False, full_html=False) }}
  1. Quel est le prix moyen du mètre carré pour les appartements à Paris en 2022 ?
#Quelle est le prix moyen d'un mètre carré dans les communes du 75 en utilisant geojson et choropleth ?
geojson_url = "https://france-geojson.gregoiredavid.fr/repo/departements/75-paris/communes-75-paris.geojson"
geojson_data = requests.get(geojson_url).json()

vf_com_paris = df[(df['code_departement'] == 75) & (df['nombre_de_lots']<6) & ((df['surface_carrez_du_1er_lot'] + df['surface_carrez_du_2eme_lot'] + df['surface_carrez_du_3eme_lot'] + df['surface_carrez_du_4eme_lot'] + df['surface_carrez_du_5eme_lot']) > 0)]
vf_com_paris['surface_totale'] = vf_com_paris['surface_carrez_du_1er_lot'] + vf_com_paris['surface_carrez_du_2eme_lot'] + vf_com_paris['surface_carrez_du_3eme_lot'] + vf_com_paris['surface_carrez_du_4eme_lot'] + vf_com_paris['surface_carrez_du_5eme_lot']
vf_com_paris['prix_m2'] = vf_com_paris['valeur_fonciere'] / vf_com_paris['surface_totale']
vf_com_paris = vf_com_paris.groupby('code_commune')['prix_m2'].mean().reset_index()
vf_com_paris['code_commune'] = '75'+vf_com_paris['code_commune'].astype(str)

fig = px.choropleth(vf_com_paris, 
                    geojson=geojson_data, 
                    locations='code_commune', 
                    color='prix_m2',
                    color_continuous_scale='Reds',
                    featureidkey='properties.code',
                    projection="mercator",
                    title="Prix moyen d'un mètre carré en €")
fig.update_geos(fitbounds="locations", visible=False)
fig.update_layout(height=600,
                  width=800);
#fig.show()
%%jmd
[//]: # (-.-|m { input: true, output_error: false, input_fold: hide })
{{ fig.to_html(include_plotlyjs=False, full_html=False) }}
  1. Nombre de bien vendu dans les 20 villes
#Nombre de bien vendu dans les 20 villes
plt.rcParams['figure.figsize'] = [18, 5]
plt.rcParams['figure.dpi'] = 150

vente_ville = df.groupby(['commune']).size().sort_values(ascending = False).head(20)
x = vente_ville.index
y = vente_ville.tolist()

plt.scatter(x, y, color='#119670', marker='o', label = 'Nombre de biens vendus')
plt.title('Nombre de biens vendus par ville', fontsize = 13)
plt.xticks(rotation=60)
plt.xlabel("Ville", fontsize = 12)
plt.ylabel("Nombre de biens vendus", fontsize = 12)
plt.legend(loc = 'upper right')
plt.grid(True)
plt.show()

Lyon

  1. Valeur foncière moyenne par arrondissement de Lyon en 2022
#Valeur foncière moyenne par arrondissement de Lyon en 2022
lyon = df.loc[(df['commune'].str.contains("LYON")) & (df['commune'].str.contains("EME"))]
arrondissement_lyon = lyon.groupby(['commune']).mean().sort_values(['valeur_fonciere'])
x = arrondissement_lyon.index
y = arrondissement_lyon['valeur_fonciere']
arrondissement_lyon.head()
no_disposition valeur_fonciere no_voie code_postal code_commune no_plan surface_carrez_du_1er_lot surface_carrez_du_2eme_lot surface_carrez_du_3eme_lot surface_carrez_du_4eme_lot surface_carrez_du_5eme_lot nombre_de_lots code_type_local surface_reelle_bati nombre_pieces_principales surface_terrain
commune
LYON 5EME 1.003717 3.993009e+05 48.791822 69005.0 385.0 66.944238 30.991945 19.413160 0.621611 0.314449 0.000000 1.584882 2.065675 75.116481 3.003717 35.422553
LYON 8EME 1.002094 4.994212e+05 57.570133 69008.0 388.0 90.849965 54.388974 13.122826 0.515052 0.100251 0.000000 1.325192 2.142359 68.923238 2.542219 39.972087
LYON 4EME 1.025707 7.098798e+05 28.996144 69004.0 384.0 83.394602 32.994254 13.620206 1.237352 0.241915 0.000000 1.525707 2.174807 68.169666 2.517995 41.029563
LYON 2EME 1.025260 1.004394e+06 24.36107 69002.0 382.0 59.179792 38.735765 11.226345 3.079480 0.466330 1.040669 1.496285 2.326895 80.313522 2.142645 24.613670
LYON 7EME 1.002449 1.100286e+06 59.290402 69007.0 387.0 85.114594 33.955132 8.728790 0.690700 0.288834 0.012684 1.299706 2.265426 87.515181 2.101371 44.467189
#Valeur foncière moyenne par arrondissement de Lyon en 2022
plt.figure(1, figsize=(10, 6))
plt.plot(x, y, color='purple', marker='o', label='Prix moyen')
plt.title('Valeur fonciere moyenne par arrondissement de Lyon en 2021')
plt.xticks(rotation=80)
plt.xlabel("Arrondissements de Lyon")
plt.ylabel("Valeur fonciere moyenne")
plt.legend(loc = 'upper left')
plt.grid(True)
plt.show()
  1. Nombre de biens vendus par arrondissement de Lyon
# Nombre de biens vendus par arrondissement de Lyon
arrondissement_lyon = lyon.groupby(['commune']).size()
x = arrondissement_lyon.index.tolist()
y = arrondissement_lyon.tolist()
 
plt.figure(1, figsize=(10, 6))
plt.pie(y, labels = x, autopct = '%.2f')
plt.title("Pars de biens vendus par arrondissement à Lyon")

# Ajout d'un cercle au centre
my_circle=plt.Circle( (0,0), 0.7, color='white')
p=plt.gcf()
p.gca().add_artist(my_circle)

plt.show()

Relation diverse

  1. Nature des cultures en France en 2022
# Nature des cultures en France en 2022
plt.figure(figsize=(8,8))
plt.title('Nature des cultures françaises en 2022')
sns.set(rc={'figure.figsize':(3,9)})
sns.countplot(y='nature_culture', data=df)
<AxesSubplot:title={'center':'Nature des cultures françaises en 2022'}, xlabel='count', ylabel='nature_culture'>
  1. Relation entre valeur foncière et nature de mutation
# Relation entre valeur foncière et nature de mutation
df_ve = df[df['nature_mutation']=='Vente'].sort_values(by='valeur_fonciere', ascending=False)
se_vente_commune = df_ve.commune.value_counts()
plt.figure(figsize=(10,5))

df_best_city = df_ve[df_ve['commune'].isin(se_vente_commune.head(10).index)]
df_best_city = df_best_city[df_best_city['valeur_fonciere'] < df_best_city['valeur_fonciere'].quantile(.95)]
df_best_city = df_best_city[df_best_city['valeur_fonciere'] > df_best_city['valeur_fonciere'].quantile(.05)]
order=df_best_city['valeur_fonciere'].groupby(df_best_city.commune).median().sort_values(ascending=False)
plt.figure(figsize=(20,10))
ax = sns.boxplot(x=df_best_city.commune, y=df_best_city['valeur_fonciere'], order=order.index)

ax.set_title('Relation entre la commune et la valeur foncière')
ax.set_xlabel('Commune')
ax.set_ylabel('Valeur foncière')
Text(0, 0.5, 'Valeur foncière')
<Figure size 1500x750 with 0 Axes>
  1. Relation entre nombre de pièce principale et nature de mutation
# Relation entre nombre de pièce principale et nature de mutation

df_ve = df[df['nature_mutation']=='Vente'].sort_values(by='valeur_fonciere', ascending=False)
se_vente_nombre_pieces_principales = df_ve.nombre_pieces_principales.value_counts()
plt.figure(figsize=(10,5))

df_best_city = df_ve[df_ve['nombre_pieces_principales'].isin(se_vente_nombre_pieces_principales.head(10).index)]
df_best_city = df_best_city[df_best_city['valeur_fonciere'] < df_best_city['valeur_fonciere'].quantile(.95)]
df_best_city = df_best_city[df_best_city['valeur_fonciere'] > df_best_city['valeur_fonciere'].quantile(.05)]
order=df_best_city['valeur_fonciere'].groupby(df_best_city.nombre_pieces_principales).median().sort_values(ascending=False)
plt.figure(figsize=(20,10))
ax = sns.boxplot(x=df_best_city.nombre_pieces_principales, y=df_best_city['valeur_fonciere'], order=order.index)

ax.set_title('Relation entre le nombre pièce principale et la valeur foncière')
ax.set_xlabel('nombre_pieces_principales')
ax.set_ylabel('Valeur foncière')
Text(0, 0.5, 'Valeur foncière')
<Figure size 1500x750 with 0 Axes>
  1. Prix au m² selon le type de local ?
# Prix au mètre carré en fonction du type de local
pm2 = df[(df['nombre_de_lots']<6) & ((df['surface_carrez_du_1er_lot'] + df['surface_carrez_du_2eme_lot'] + df['surface_carrez_du_3eme_lot'] + df['surface_carrez_du_4eme_lot'] + df['surface_carrez_du_5eme_lot']) > 0)]
pm2['surface_totale'] = pm2['surface_carrez_du_1er_lot'] + pm2['surface_carrez_du_2eme_lot'] + pm2['surface_carrez_du_3eme_lot'] + pm2['surface_carrez_du_4eme_lot'] + pm2['surface_carrez_du_5eme_lot']
pm2['prix_m2'] = pm2['valeur_fonciere'] / pm2['surface_totale']
sns.set(rc={'figure.figsize':(40,30)})
sns.color_palette()
sns.catplot(x="type_local", y="prix_m2", jitter=True, data=pm2, kind="strip", height=8, aspect=1.5, palette="Set2")
<seaborn.axisgrid.FacetGrid at 0x7f95e8b69430>
  1. Prix au m² selon le nombre de pièce principale
# Prix au mètre carré en fonction du nombre de pièce principale
pm2 = df[(df['nombre_de_lots']<6) & ((df['surface_carrez_du_1er_lot'] + df['surface_carrez_du_2eme_lot'] + df['surface_carrez_du_3eme_lot'] + df['surface_carrez_du_4eme_lot'] + df['surface_carrez_du_5eme_lot']) > 0)]
pm2['surface_totale'] = pm2['surface_carrez_du_1er_lot'] + pm2['surface_carrez_du_2eme_lot'] + pm2['surface_carrez_du_3eme_lot'] + pm2['surface_carrez_du_4eme_lot'] + pm2['surface_carrez_du_5eme_lot']
pm2['prix_m2'] = pm2['valeur_fonciere'] / pm2['surface_totale']
pm2['nombre_pieces_principales'] = pm2['nombre_pieces_principales'].astype('category')
sns.set(rc={'figure.figsize':(40,30)})
sns.color_palette()
sns.catplot(x="nombre_pieces_principales", y="prix_m2", jitter=True, data=pm2, kind="strip", height=8, aspect=1.5, palette="Set2")
<seaborn.axisgrid.FacetGrid at 0x7f953b20ef10>

IV ] Comparaison avec 2021

Importation et nettoyage des données de 2021

df2021 = pd.read_csv('https://static.data.gouv.fr/resources/demandes-de-valeurs-foncieres/20230405-155828/valeursfoncieres-2021.txt', sep = '|')
df2021 = df2021.rename(columns=lambda x: x.replace(' ', '_').lower())
df2021 = df2021.drop(['identifiant_de_document',
              'reference_document',
              '1_articles_cgi',
              '2_articles_cgi',
              '3_articles_cgi',
              '4_articles_cgi',
              '5_articles_cgi',
              'prefixe_de_section',
              'no_volume',
              '3eme_lot',
              '4eme_lot',
              '5eme_lot',
              'identifiant_local',
              'nature_culture_speciale',
              ],axis= 1)
# suppression des doubles
df2021 = df2021.drop_duplicates()
# suppression des ligne null
df2021 = df2021.dropna(how="all")
df2021 = df2021.drop_duplicates()

df2021['valeur_fonciere'] = df2021['valeur_fonciere'].str.replace(',', '.').astype('float')
df2021['surface_carrez_du_1er_lot'] = df2021['surface_carrez_du_1er_lot'].str.replace(',', '.').astype('float')
df2021['surface_carrez_du_2eme_lot'] = df2021['surface_carrez_du_2eme_lot'].str.replace(',', '.').astype('float')
df2021['surface_carrez_du_3eme_lot'] = df2021['surface_carrez_du_3eme_lot'].str.replace(',', '.').astype('float')
df2021['surface_carrez_du_4eme_lot'] = df2021['surface_carrez_du_4eme_lot'].str.replace(',', '.').astype('float')
df2021['surface_carrez_du_5eme_lot'] = df2021['surface_carrez_du_5eme_lot'].str.replace(',', '.').astype('float')
df2021.info()
df2021.head()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 4303715 entries, 0 to 4649208
Data columns (total 29 columns):
 #   Column                      Dtype  
---  ------                      -----  
 0   no_disposition              int64  
 1   date_mutation               object 
 2   nature_mutation             object 
 3   valeur_fonciere             float64
 4   no_voie                     float64
 5   b/t/q                       object 
 6   type_de_voie                object 
 7   code_voie                   object 
 8   voie                        object 
 9   code_postal                 float64
 10  commune                     object 
 11  code_departement            object 
 12  code_commune                int64  
 13  section                     object 
 14  no_plan                     int64  
 15  1er_lot                     object 
 16  surface_carrez_du_1er_lot   float64
 17  2eme_lot                    object 
 18  surface_carrez_du_2eme_lot  float64
 19  surface_carrez_du_3eme_lot  float64
 20  surface_carrez_du_4eme_lot  float64
 21  surface_carrez_du_5eme_lot  float64
 22  nombre_de_lots              int64  
 23  code_type_local             float64
 24  type_local                  object 
 25  surface_reelle_bati         float64
 26  nombre_pieces_principales   float64
 27  nature_culture              object 
 28  surface_terrain             float64
dtypes: float64(12), int64(4), object(13)
memory usage: 985.0+ MB
no_disposition date_mutation nature_mutation valeur_fonciere no_voie b/t/q type_de_voie code_voie voie code_postal ... surface_carrez_du_3eme_lot surface_carrez_du_4eme_lot surface_carrez_du_5eme_lot nombre_de_lots code_type_local type_local surface_reelle_bati nombre_pieces_principales nature_culture surface_terrain
0 1 05/01/2021 Vente 185000.0 5080.0 NaN CHE 0471 DE VOGELAS 1370.0 ... NaN NaN NaN 0 1.0 Maison 97.0 5.0 S 2410.0
1 1 05/01/2021 Vente 185000.0 5080.0 NaN CHE 0471 DE VOGELAS 1370.0 ... NaN NaN NaN 0 3.0 Dépendance 0.0 0.0 S 2410.0
2 1 06/01/2021 Vente 10.0 NaN NaN NaN B043 ROUGEMONT 1290.0 ... NaN NaN NaN 0 NaN NaN NaN NaN BT 530.0
3 1 04/01/2021 Vente 204332.0 7.0 NaN ALL 0276 DES ECUREUILS 1310.0 ... NaN NaN NaN 0 1.0 Maison 88.0 4.0 S 866.0
4 1 06/01/2021 Vente 320000.0 87.0 NaN RTE 0140 DE CERTINES 1250.0 ... NaN NaN NaN 0 1.0 Maison 168.0 5.0 S 1426.0

5 rows × 29 columns

  1. Comparaison du prix moyen du mètre carré entre 2021 et 2022
pm2_22 = df[(df['nombre_de_lots']<6) & ((df['surface_carrez_du_1er_lot'] + df['surface_carrez_du_2eme_lot'] + df['surface_carrez_du_3eme_lot'] + df['surface_carrez_du_4eme_lot'] + df['surface_carrez_du_5eme_lot']) > 0)]
pm2_22['surface_totale'] = pm2_22['surface_carrez_du_1er_lot'] + pm2_22['surface_carrez_du_2eme_lot'] + pm2_22['surface_carrez_du_3eme_lot'] + pm2_22['surface_carrez_du_4eme_lot'] + pm2_22['surface_carrez_du_5eme_lot']
pm2_22['prix_m2'] = pm2_22['valeur_fonciere'] / pm2_22['surface_totale']

pm2_21 = df2021[(df2021['nombre_de_lots']<6) & ((df2021['surface_carrez_du_1er_lot'] + df2021['surface_carrez_du_2eme_lot'] + df2021['surface_carrez_du_3eme_lot'] + df2021['surface_carrez_du_4eme_lot'] + df2021['surface_carrez_du_5eme_lot']) > 0)]
pm2_21['surface_totale'] = pm2_21['surface_carrez_du_1er_lot'] + pm2_21['surface_carrez_du_2eme_lot'] + pm2_21['surface_carrez_du_3eme_lot'] + pm2_21['surface_carrez_du_4eme_lot'] + pm2_21['surface_carrez_du_5eme_lot']
pm2_21['prix_m2'] = pm2_21['valeur_fonciere'] / pm2_21['surface_totale']


fig = plt.figure(11,figsize = (10,5))
axis = fig.add_subplot(1, 1, 1)
axis.set_title('Comparaison du prix du mètre carré entre 2021 et 2022')
axis.set_xlabel("Année")
axis.set_ylabel("Prix")

gb1 = pm2_21['prix_m2'].mean()

gb2 = pm2_22['prix_m2'].mean()

com = ['2021','2022']
prix = [gb1,gb2]

axis.bar(com[:],prix[:])

plt.xticks(ticks=com,labels=prix,rotation=0)
fig.show()

On remarque que la valeur foncière moyenne est plus haute sur l'année 2022 que sur l'année 2021.

  1. Comparaison de la nature des mutations
mutation_type2021 = df2021['nature_mutation']
r2021 = Counter(mutation_type2021)
mutation_type2022 = df['nature_mutation']
r2022 = Counter(mutation_type2022)


mutation_df2021 = pd.DataFrame.from_dict(r2021, orient='index').sort_values(by=0)
mutation_df2021.columns = ['nature_mutation']

mutation_df2022 = pd.DataFrame.from_dict(r2022, orient='index').sort_values(by=0)
mutation_df2022.columns = ['nature_mutation']

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(32, 8))

mutation_df2021.plot.pie(y = 'nature_mutation', 
                 colormap = 'Reds_r',
                 figsize = (16, 16),
                 fontsize = 9, 
                 autopct = '%.2f',
                 legend = False,
                 title = 'Diagramme de distribution de la nature des mutations en 2021',
                 ax=ax1)


mutation_df2022.plot.pie(y = 'nature_mutation', 
                 colormap = 'Reds_r',
                 figsize = (16, 16),
                 fontsize = 9, 
                 autopct = '%.2f',
                 legend = False,
                 title = 'Diagramme de distribution de la nature des mutations 2022',
                 ax=ax2,
                 )



# Ajouter un cercle au centre pour faire un donut chart
mutation_df2021=plt.Circle( (0,0), 0.7, color='white')
mutation_df2022=plt.Circle( (0,0), 0.7, color='white')

plt.show()

On remarque qu'il n'y a presque pas de différence dans la répartition des natures de mutation entre l'année 2021 et 2022

On créé un nouveau dataframe rassemblant les donnée de 2021 et 2022

df['year'] = int(2022)
df2021['year'] = int(2021)
all_years = [ df, df2021]
data_years = pd.concat(all_years)
  1. Surface du terrain en fonction du département entre 2021 et 2022
fig = plt.figure(4,figsize = (35,10))
axis = fig.add_subplot(1, 1, 1)
axis.set_title('Surface du terrain en fonction du département entre 2021 et 2022')
axis.set_xlabel("Département")
axis.set_ylabel("Surface du terrain")
gb = data_years.loc[data_years["code_departement"].notna(),("surface_terrain","code_departement","year")]

ax = sns.barplot(x=gb["code_departement"].astype(str), y=gb["surface_terrain"], hue="year", data = gb)

plt.xticks(rotation=90)
fig.show()
  1. Relation entre la commune et la valeur foncière en fonction de la commune
sns.set_theme(style="ticks", palette="pastel")
df_ve = data_years[data_years['nature_mutation']=='Vente'].sort_values(by='valeur_fonciere', ascending=False)
se_vente_commune = df_ve.commune.value_counts()
plt.figure(figsize=(10,5))

df_best_city = df_ve[df_ve['commune'].isin(se_vente_commune.head(10).index)]
df_best_city = df_best_city[df_best_city['valeur_fonciere'] < df_best_city['valeur_fonciere'].quantile(.95)]
df_best_city = df_best_city[df_best_city['valeur_fonciere'] > df_best_city['valeur_fonciere'].quantile(.05)]
order=df_best_city['valeur_fonciere'].groupby(df_best_city.commune).median().sort_values(ascending=False)
plt.figure(figsize=(20,10))
ax = sns.boxplot(x=df_best_city.commune, y=df_best_city['valeur_fonciere'], hue=df_best_city['year'], order=order.index, palette=["m", "g"],)
plt.legend(loc = 'upper right')

ax.set_title('Relation entre la commune et la valeur foncière')
ax.set_xlabel('Commune')
ax.set_ylabel('Valeur foncière')
Text(0, 0.5, 'Valeur foncière')
<Figure size 1500x750 with 0 Axes>
  1. Nombre de biens vendus par commune en 2022 et 2021
sns.set_style('whitegrid')
sns.set(rc={'figure.figsize':(18,5), 'figure.dpi': 150})

vente_ville = data_years.groupby(['commune','year']).size().sort_values(ascending=False).head(20).reset_index(name='nombre')
ax = sns.scatterplot(x='commune', y='nombre', hue='year', data=vente_ville, color='#119670', palette=["b", "r"], marker='o')
ax.set_title('Nombre de biens vendus par commune en 2022 et 2021')
ax.set_xlabel('Commune')
ax.set_ylabel('Nombre de biens')
Text(0, 0.5, 'Nombre de biens')

On remarque que les villes dans lequel le plus de transaction ont été fait sont différente entre 2021 et 2022


*Licence :* Licence Ouverte / Open Licence version 2.0

Logo-licence-ouverte2.png